local ffi = require "ffi"

ffi.cdef [[
    typedef struct { float x, y, z; } vec3_t;
]]

local vec3_t = ffi.typeof("vec3_t")

local vec3
local mt = {
    ---@param _ vec3
    ---@param x number
    ---@param y number
    ---@param z number
    ---@return vec3
    __new = function(_, x, y, z)
        if x and y and z then
            return ffi.new(vec3_t, { x, y, z })
        elseif x and not (y or z) then
            return ffi.new(vec3_t, { x, x, x })
        elseif not (x or y or z) then
            return ffi.new(vec3_t, { 0, 0, 0 })
        end

        error("x, y, z must be not a nil")
    end,

    ---@param self vec3
    ---@param other vec3
    ---@return vec3
    __add = function(self, other)
        return vec3(self.x + other.x, self.y + other.y, self.z + other.z)
    end,

    ---@param self vec3
    ---@param other vec3
    ---@return vec3
    __sub = function(self, other)
        return vec3(self.x - other.x, self.y - other.y, self.z - other.z)
    end,

    ---@param self vec3
    ---@param other vec3|number
    ---@return vec3
    __mul = function(self, other)
        if type(other) == "number" then
            return vec3(self.x * other, self.y * other, self.z * other)
        end

        return vec3(self.x * other.x, self.y * other.y, self.z * other.z)
    end,

    ---@param self vec3
    ---@param other vec3|number
    ---@return vec3
    __div = function(self, other)
        if type(other) == "number" then
            return vec3(self.x / other, self.y / other, self.z / other)
        end

        return vec3(self.x / other.x, self.y / other.y, self.z / other.z)
    end,

    ---@param self vec3
    ---@param other vec3
    __eq = function(self, other)
        return self.x == other.x and self.y == other.y and self.z == other.z
    end,

    __len = function(_)
        return 3
    end,

    __tostring = function(self)
        return "vec3(" .. self.x .. ", " .. self.y .. ", " .. self.z .. ")"
    end,

    ---@param self vec3
    ---@return vec3
    copy = function(self) return vec3(self.x, self.y, self.z) end,
}

---@class vec3
---@field x number
---@field y number
---@field z number
---@operator call:vec3
---@operator add:vec3
---@operator sub:vec3
---@operator mul:vec3
---@operator div:vec3
vec3 = ffi.metatype("vec3_t", mt)

return vec3
